package org.colcon.web.server;

import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.colcon.web.model.ColCon;
import org.colcon.web.model.News;
import org.colcon.web.model.framework.ICommand;
import org.colcon.web.model.framework.IContainer;
import org.colcon.web.model.framework.IGroup;
import org.colcon.web.model.framework.IItem;
import org.colcon.web.model.framework.INews;
import org.colcon.web.model.framework.IUser;
import org.colcon.web.model.parsers.CLIParser;
import org.colcon.web.model.parsers.XMLParser;

public class Model
{
	public static final String
	
		NULL = "null",
		
		ROOT = "colcon",
	
		NEWS = "news",
		USERS = "users",
		GROUPS = "groups",
		COMMANDS = "commands",
		
		// groups
		CONTRIBUTOR_CONGRESS = "contributor_congress",
		GROUP_CONGRESS = "group_congress",
		USER_CONGRESS = "user_congress",
		
		// users
		ADMIN = "admin",
		
		// commands
		HELP = "help",
		GET = "get",
		SET = "set",
		ADD = "add",
		REMOVE = "remove",
		OPEN = "open",
		ASSIGN = "assign",
	
		// command arguments
		CMD = "cmd",
		PATH = "path",
		TYPE = "type",
		USER = "user";
	
	private static Map<String, ICommand> commands;
	
	private static Map<String, IItem> items;
	
	private static int nextId = 0;
	
	public static void init()
	{
		System.out.println("~ collective congress ~");
		System.out.println(new Date());
		
		commands = new LinkedHashMap<String, ICommand>();
		items = new HashMap<String, IItem>();
		
		for(IItem item : XMLParser.loadItems("xml/items/commands.xml"))
			addCommand((ICommand) item);
		
		add(new ColCon());
	}
	
	public static ColCon getRoot()
	{
		IItem item = get(ROOT);
		
		if(item == null || !(item instanceof ColCon))
			return null;
		
		return (ColCon)item;
	}
	
	public static ICommand[] getCommands()
	{
		return commands.values().toArray(new ICommand[0]);
	}
	
	public static ICommand getCommand(String id)
	{
		return commands.get(id);
	}
	
	public static boolean hasCommand(String id)
	{
		return commands.containsKey(id);
	}
	
	public static boolean addCommand(ICommand command)
	{
		if(command == null || hasCommand(command.getId()))
			return false;
		
		commands.put(command.getPath(), command);
		
		return commands.containsKey(command.getId());
	}
	
	public static boolean removeCommand(String id)
	{
		if(!hasCommand(id))
			return false;
		
		commands.remove(id);
		
		return !commands.containsKey(id);
	}
	
	public static IItem get(String path)
	{
		return items.get(path);
	}
	
	public static boolean has(String path)
	{
		return items.containsKey(path);
	}
	
	public static boolean add(IItem item)
	{
		if(item == null)
			return false;
		
		String id = item.getId();
		
		if(id == null)
			setId(item);
		
		String path = item.getPath();
		
		if(path == null || path.trim().isEmpty() || has(path))
			return false;
		
		if(!item.isRoot())
		{
			IItem parent = get(item.getParentPath());
			
			if(parent == null || !(parent instanceof IContainer))
				return false;
			
			((IContainer)parent).addChild(item.getId());
		}
		
		setDate(item);
		
		items.put(item.getPath(), item);
		
		init(item);
		
		return items.containsKey(path);
	}
	
	//TODO: make init commands inheritable from superclasses
	private static void init(IItem item)
	{
		String[] commands = XMLParser.loadInitCommands(item.getType());
		
		Map<String, String> args = item.toMap();
		
		if(commands != null)
		{
			for(String command : commands)
			{
				for(String key : args.keySet())
				{
					command = command.replaceAll("\\[" + key + "\\]", args.get(key));
				}
				
				CLI.execute(command);
			}
		}
	}
	
	public static boolean remove(String path)
	{
		if(!has(path))
			return false;
		
		items.remove(path);
		
		return !has(path);
	}
	
	public static IItem[] open(String path)
	{
		if(!has(path))
			return null;
		
		IItem item = get(path);
		
		if(item == null || !(item instanceof IContainer))
			return null;
		
		String[] children = ((IContainer)item).getChildren();
		
		IItem[] items = new IItem[children.length];
		
		for(int i = 0; i < items.length; i++)
			items[i] = get(path + '/' + children[i]);
		
		return items;
	}
	
	public static IUser getUser(String id)
	{
		return (IUser) get(getRoot().getUserPath(id));
	}
	
	public static IGroup getGroup(String id)
	{
		return (IGroup) get(getRoot().getGroupPath(id));
	}
	
	public static IUser[] getUsers()
	{
		return (IUser[]) open(getRoot().users());
	}
	
	public static IGroup[] getGroup()
	{
		return (IGroup[]) open(getRoot().groups());
	}
	
	public static void setDate(IItem item)
	{
		item.setDate(new Date());
	}
	
	public static Date addToDate(Date date, int days)
	{
		Date newDate = new Date();
		newDate.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
		return newDate;
	}
	
	public static boolean logCommand(String command)
	{
		System.out.println("[log] " + command);
		
		String cmd = CLIParser.parseCmd(command);
		
		// these commands are not added to news
		if(cmd.equals(CLI.NULL) ||
				cmd.equals(CLI.HELP) ||
				cmd.equals(CLI.PRINT) ||
				cmd.equals(CLI.GET) ||
				cmd.equals(CLI.OPEN))
			return true;
		
		INews news = new News(command);
		
		return add(news);
	}
	
	public static void setId(IItem item)
	{
		item.setId(item.getType() + '_' + nextId++);
	}
}
